#ifndef __FPGA_CACHE_H__
#define __FPGA_CACHE_H__

#include <hls_stream.h>
#include "graph_fpga.h"


unsigned long merge_decode_function8(ap_uint<8> &opcode){
	#pragma HLS INLINE
	const unsigned long lutIdx[256] = {
		36650387592,
		105369864320,
		105369864321,
		174089340944,
		105369864322,
		174089340960,
		174089340961,
		242808816144,
		105369864323,
		174089340976,
		174089340977,
		242808816400,
		174089340978,
		242808816416,
		242808816417,
		311528272400,
		105369864324,
		174089340992,
		174089340993,
		242808816656,
		174089340994,
		242808816672,
		242808816673,
		311528276496,
		174089340995,
		242808816688,
		242808816689,
		311528276752,
		242808816690,
		311528276768,
		311528276769,
		380247486992,
		105369864325,
		174089341008,
		174089341009,
		242808816912,
		174089341010,
		242808816928,
		242808816929,
		311528280592,
		174089341011,
		242808816944,
		242808816945,
		311528280848,
		242808816946,
		311528280864,
		311528280865,
		380247552528,
		174089341012,
		242808816960,
		242808816961,
		311528281104,
		242808816962,
		311528281120,
		311528281121,
		380247556624,
		242808816963,
		311528281136,
		311528281137,
		380247556880,
		311528281138,
		380247556896,
		380247556897,
		448963818000,
		105369864326,
		174089341024,
		174089341025,
		242808817168,
		174089341026,
		242808817184,
		242808817185,
		311528284688,
		174089341027,
		242808817200,
		242808817201,
		311528284944,
		242808817202,
		311528284960,
		311528284961,
		380247618064,
		174089341028,
		242808817216,
		242808817217,
		311528285200,
		242808817218,
		311528285216,
		311528285217,
		380247622160,
		242808817219,
		311528285232,
		311528285233,
		380247622416,
		311528285234,
		380247622432,
		380247622433,
		448964866576,
		174089341029,
		242808817232,
		242808817233,
		311528285456,
		242808817234,
		311528285472,
		311528285473,
		380247626256,
		242808817235,
		311528285488,
		311528285489,
		380247626512,
		311528285490,
		380247626528,
		380247626529,
		448964932112,
		242808817236,
		311528285504,
		311528285505,
		380247626768,
		311528285506,
		380247626784,
		380247626785,
		448964936208,
		311528285507,
		380247626800,
		380247626801,
		448964936464,
		380247626802,
		448964936480,
		448964936481,
		517649740304,
		105369864327,
		174089341040,
		174089341041,
		242808817424,
		174089341042,
		242808817440,
		242808817441,
		311528288784,
		174089341043,
		242808817456,
		242808817457,
		311528289040,
		242808817458,
		311528289056,
		311528289057,
		380247683600,
		174089341044,
		242808817472,
		242808817473,
		311528289296,
		242808817474,
		311528289312,
		311528289313,
		380247687696,
		242808817475,
		311528289328,
		311528289329,
		380247687952,
		311528289330,
		380247687968,
		380247687969,
		448965915152,
		174089341045,
		242808817488,
		242808817489,
		311528289552,
		242808817490,
		311528289568,
		311528289569,
		380247691792,
		242808817491,
		311528289584,
		311528289585,
		380247692048,
		311528289586,
		380247692064,
		380247692065,
		448965980688,
		242808817492,
		311528289600,
		311528289601,
		380247692304,
		311528289602,
		380247692320,
		380247692321,
		448965984784,
		311528289603,
		380247692336,
		380247692337,
		448965985040,
		380247692338,
		448965985056,
		448965985057,
		517666517520,
		174089341046,
		242808817504,
		242808817505,
		311528289808,
		242808817506,
		311528289824,
		311528289825,
		380247695888,
		242808817507,
		311528289840,
		311528289841,
		380247696144,
		311528289842,
		380247696160,
		380247696161,
		448966046224,
		242808817508,
		311528289856,
		311528289857,
		380247696400,
		311528289858,
		380247696416,
		380247696417,
		448966050320,
		311528289859,
		380247696432,
		380247696433,
		448966050576,
		380247696434,
		448966050592,
		448966050593,
		517667566096,
		242808817509,
		311528289872,
		311528289873,
		380247696656,
		311528289874,
		380247696672,
		380247696673,
		448966054416,
		311528289875,
		380247696688,
		380247696689,
		448966054672,
		380247696690,
		448966054688,
		448966054689,
		517667631632,
		311528289876,
		380247696704,
		380247696705,
		448966054928,
		380247696706,
		448966054944,
		448966054945,
		517667635728,
		380247696707,
		448966054960,
		448966054961,
		517667635984,
		448966054962,
		517667636000,
		517667636001,
		586100781584
	};
	return lutIdx[opcode];
};

typedef struct {
	uint						offset;
	ap_uint<32>					active_flag;
    ap_uint<1>                  flag;
} csrIndex_mem_request;
typedef struct {
	ap_uint<1>					select;
	uint						offset;
	ap_uint<512>				data;
    ap_uint<1>                  flag;
} csrIndex_select;
void bitMapRead(uint16                             *bitMap,
		hls::stream<csrIndex_mem_request>         (&csrIndexMemRequestStream)[2],
		hls::stream<csrIndex_select>        &csrIndexSelectStream,
		int                srcStart,
		int                srcEnd){
#define BITMAP_BUFFER_SIZE 128
	ap_uint<512> bitMapBuffer[BITMAP_BUFFER_SIZE];

	int blockStart = srcStart/512;
	int blockEnd   = (srcEnd%512)?(srcEnd/512+1):srcEnd/512;
	int blockNum = blockEnd - blockStart;
	for(int i=0;i<blockNum;i+=BITMAP_BUFFER_SIZE){
		int blksize=((blockNum-i)<BITMAP_BUFFER_SIZE)?(blockNum-i):BITMAP_BUFFER_SIZE;
		for(int j=0;j<blksize;j++){
			#pragma HLS PIPELINE II=1
			bitMapBuffer[j]=bitMap[blockStart+i+j];
		}
		for(int j=0;j<blksize;j++){
			#pragma HLS PIPELINE II=1
			ap_uint<512> tmp=bitMapBuffer[j];
			if(tmp!=0){
				ap_uint<32> active_flag;
				for(int k=0;k<32;k++){
					#pragma HLS UNROLL
					active_flag.range(k,k)=(tmp.range(k*16+15,k*16)!=0)?1:0;
				}

				csrIndex_mem_request out;
				out.active_flag=active_flag;
				out.offset=(blockStart+i+j)*512;
				out.flag=0;
				write_to_stream(csrIndexMemRequestStream[(blockStart+i+j)%2], out);

				csrIndex_select out2;
				out2.offset=(blockStart+i+j)*512;
				out2.data=tmp;
				out2.select=(blockStart+i+j)%2;
				out2.flag=0;
				write_to_stream(csrIndexSelectStream, out2);
			}
		}
	}
	{
		csrIndex_mem_request out;
		out.active_flag=0;
		out.offset=0;
		out.flag=1;
		write_to_stream(csrIndexMemRequestStream[0], out);
		write_to_stream(csrIndexMemRequestStream[1], out);
		//write_to_stream(csrIndexMemRequestStream[2], out);
		//write_to_stream(csrIndexMemRequestStream[3], out);

		csrIndex_select out2;
		out2.offset=0;
		out2.data=0;
		out2.select=0;
		out2.flag=1;
		write_to_stream(csrIndexSelectStream, out2);
	}
}

typedef struct {
	uint						  offset;
	ap_uint<16>    			      data;
	ap_uint<512>				  csrIndex;
    ap_uint<1>                    flag;
} bitmap_block16_withIndex;

#define NODE_MEM_READ_BURST_SIZE 32
#define EDGE_MEM_READ_BURST_SIZE 32

void csrIndexRead(uint16					*csrIndex,
		hls::stream<csrIndex_mem_request>         &inputStream,
		hls::stream<uint16>         &outputStream){
#pragma HLS function_instantiate variable=inputStream

	ap_uint<512> buffer[NODE_MEM_READ_BURST_SIZE];
	//int node_ideal_count=0;
	//int node_true_count=0;
	while(true){
		csrIndex_mem_request in;
		read_from_stream(inputStream, in);
		if(in.flag==1){
			break;
		}
		for(int i=0;i<NODE_MEM_READ_BURST_SIZE;i++){
			#pragma HLS PIPELINE II=1
			buffer[i]=csrIndex[in.offset/16/64*NODE_MEM_READ_BURST_SIZE+i];
		}
		for(int i=0;i<NODE_MEM_READ_BURST_SIZE;i++){
			#pragma HLS PIPELINE II=1
			uint16 tmp=buffer[i];
			if(in.active_flag.range(i,i)) write_to_stream(outputStream, tmp);
		}

		//node_true_count+=NODE_MEM_READ_BURST_SIZE;
	}

	//printf("node_ideal_count:%d node_true_count:%d\n",node_ideal_count,node_true_count);
}

void csrIndexReadMerge(hls::stream<uint16>         (&inputStream)[2],
		hls::stream<csrIndex_select>         &csrIndexSelectStream,
		hls::stream<bitmap_block16_withIndex>         &outputStream){

	while(true){
		csrIndex_select in;
		read_from_stream(csrIndexSelectStream, in);
		if(in.flag==1){
			break;
		}
		for(int i=0;i<32;i++){
			#pragma HLS PIPELINE II=1
			if(in.data.range(i*16+15,i*16)){
				uint16 tmp;
				read_from_stream(inputStream[in.select], tmp);
				bitmap_block16_withIndex out;
				out.csrIndex=tmp;
				out.data=in.data.range(i*16+15,i*16);
				out.offset=in.offset+i*16;
				out.flag=0;
				write_to_stream(outputStream, out);
				//printf("select:%d offset:%d\n",(int)in.select,tmp.offset);
			}
		}
	}
	{
		bitmap_block16_withIndex out;
		out.csrIndex=0;
		out.data=0;
		out.offset=0;
		out.flag=1;
		write_to_stream(outputStream, out);
	}

	//printf("node_ideal_count:%d node_true_count:%d\n",node_ideal_count,node_true_count);
}

typedef struct{
	uint						  offset;
	ap_uint<16>    			      data;
	ap_uint<32*4>				  csr_begin;
	ap_uint<32*4>				  csr_end;
	ap_uint<4>					  csr_state;
	ap_uint<1>                    flag;
}intervalBlock4;
typedef struct{
	uint						  offset;
	ap_uint<16>    			      data;
	ap_uint<32*2>				  csr_begin;
	ap_uint<32*2>				  csr_end;
	ap_uint<2>					  csr_state;
	ap_uint<1>                    flag;
}intervalBlock2;

void edgeRequestMerge(
		hls::stream<bitmap_block16_withIndex>         &bitMapWithIndexStream,
		hls::stream<intervalBlock4>         &intervalBlock4Stream,
		hls::stream<uint16>			&indexStream){
	while(true){
#pragma HLS PIPELINE II=2
		bitmap_block16_withIndex in;
		read_from_stream(bitMapWithIndexStream, in);
		if(in.flag==1){
			break;
		}
		ap_uint<512> csrbuffer;
		csrbuffer=in.csrIndex;

		ap_uint<32> l1_begin[16];
		#pragma HLS ARRAY_PARTITION variable=l1_begin dim=0 complete
		ap_uint<32> l1_end[16];
		#pragma HLS ARRAY_PARTITION variable=l1_end dim=0 complete

		ap_uint<32> l2_begin[8];
		#pragma HLS ARRAY_PARTITION variable=l2_begin dim=0 complete
		ap_uint<32> l2_end[8];
		#pragma HLS ARRAY_PARTITION variable=l2_end dim=0 complete

		ap_uint<32> l3_begin[8];
		#pragma HLS ARRAY_PARTITION variable=l3_begin dim=0 complete
		ap_uint<32> l3_end[8];
		#pragma HLS ARRAY_PARTITION variable=l3_end dim=0 complete

		for(int i=0;i<16;i++){
			#pragma HLS UNROLL
			l1_begin[i]=csrbuffer.range(i*30+29, i*30+0);
			l1_end[i]=csrbuffer.range((i+1)*30+29, (i+1)*30+0);
		}

		for(int i=0;i<8;i++){
			#pragma HLS UNROLL
			ap_uint<32> tmp_begin=(in.data.range(2*i,2*i)==1)?l1_begin[2*i]:l1_begin[2*i+1];
			ap_uint<32> tmp_end=(in.data.range(2*i+1,2*i+1)==1)?l1_end[2*i+1]:l1_end[2*i];
			l2_begin[i]=tmp_begin;
			l2_end[i]=(tmp_end>tmp_begin)?tmp_end:tmp_begin;
		}

		ap_uint<8> opcode;
		opcode.range(0,0)=1;
		for(int i=1;i<8;i++){
			#pragma HLS UNROLL
			opcode.range(i,i)=(l2_begin[i]/EDGE_NUM==l2_end[i-1]/EDGE_NUM)?0:1;
		}

		ap_uint<40> decode_value=(ap_uint<40>)merge_decode_function8(opcode);
		ap_uint<4> decode_num=decode_value.range(39,36);
		ap_uint<4> idx[9];
#pragma HLS ARRAY_PARTITION variable=idx dim=0 complete
		for(unsigned int i=0;i<9;i++){
			#pragma HLS UNROLL
			idx[i]=decode_value.range(4*i+3,4*i);
		}

		ap_uint<8> l3_state;
		for(unsigned int i=0;i<8;i++){
			#pragma HLS UNROLL
			if(i<decode_num){
				l3_begin[i]=l2_begin[idx[i]];
				l3_end[i]=l2_end[idx[i+1]-1];
			}
			else{
				l3_begin[i]=0;
				l3_end[i]=0;
			}
			l3_state.range(i,i)=(l3_begin[i]<l3_end[i])?1:0;
		}


		intervalBlock4 out1,out2;
		out1.data=in.data;out2.data=in.data;
		out1.offset=in.offset;out2.offset=in.offset;
		out1.csr_state=l3_state.range(3,0);out2.csr_state=l3_state.range(7,4);
		out1.flag=0;out2.flag=0;
		for(int i=0;i<4;i++){
			#pragma HLS UNROLL
			out1.csr_begin.range(32*i+31,32*i)=l3_begin[i]/EDGE_NUM;
			out1.csr_end.range(32*i+31,32*i)=(l3_end[i]-1)/EDGE_NUM;
			out2.csr_begin.range(32*i+31,32*i)=l3_begin[i+4]/EDGE_NUM;
			out2.csr_end.range(32*i+31,32*i)=(l3_end[i+4]-1)/EDGE_NUM;
		}
		if(out1.csr_state) write_to_stream(intervalBlock4Stream, out1);
		if(out2.csr_state) write_to_stream(intervalBlock4Stream, out2);
		if(l3_state) write_to_stream(indexStream,in.csrIndex);
	}
	{
		intervalBlock4 out;
		out.csr_begin=0;
		out.csr_end=0;
		out.csr_state=0;
		out.data=0;
		out.offset=0;
		out.flag=1;
		write_to_stream(intervalBlock4Stream, out);
	}
}

void intervalFilter4to2(
    hls::stream<intervalBlock4>    &intervalBlock4Stream,
    hls::stream<intervalBlock2>     &intervalBlock2Stream
)
{
    while (true) {
#pragma HLS PIPELINE II=2
    	intervalBlock4 in;
    	read_from_stream(intervalBlock4Stream, in);
    	if(in.flag==1){
    		break;
    	}

    	intervalBlock2 out1,out2;
    	out1.data=in.data;out2.data=in.data;
    	out1.offset=in.offset;out2.offset=in.offset;
    	out1.csr_state=in.csr_state.range(1,0);out2.csr_state=in.csr_state.range(3,2);
    	out1.flag=0;out2.flag=0;

    	out1.csr_begin=in.csr_begin.range(63,0);
    	out1.csr_end=in.csr_end.range(63,0);
    	out2.csr_begin=in.csr_begin.range(127,64);
    	out2.csr_end=in.csr_end.range(127,64);

    	if(out1.csr_state) write_to_stream(intervalBlock2Stream, out1);
    	if(out2.csr_state) write_to_stream(intervalBlock2Stream, out2);
    }
    {
    	intervalBlock2 token;
    	token.csr_begin=0;
    	token.csr_end=0;
    	token.csr_state=0;
    	token.data=0;
    	token.offset=0;
    	token.flag=1;
    	write_to_stream(intervalBlock2Stream, token);
    }
}
void intervalFilter2to1(
    hls::stream<intervalBlock2>    &intervalBlock2Stream,
	hls::stream<bitmap_block16_interval>         &intervalStream
)
{
    while (true) {
#pragma HLS PIPELINE II=2
    	intervalBlock2 in;
    	read_from_stream(intervalBlock2Stream, in);
    	if(in.flag==1){
    		break;
    	}

    	bitmap_block16_interval out1,out2;
    	out1.data=in.data;out2.data=in.data;
    	out1.offset=in.offset;out2.offset=in.offset;
    	//out1.csr_state=in.csr_state.range(0,0);out2.csr_state=in.csr_state.range(1,1);
    	out1.flag=0;out2.flag=0;

    	out1.csr_begin=in.csr_begin.range(31,0);
    	out1.csr_end=in.csr_end.range(31,0);
    	out2.csr_begin=in.csr_begin.range(63,32);
    	out2.csr_end=in.csr_end.range(63,32);

    	if(in.csr_state.range(0,0)) write_to_stream(intervalStream, out1);
    	if(in.csr_state.range(1,1)) write_to_stream(intervalStream, out2);
    }
    {
    	bitmap_block16_interval token;
    	token.csr_begin=0;
    	token.csr_end=0;
    	token.data=0;
    	token.offset=0;
    	token.flag=1;
    	write_to_stream(intervalStream, token);
    }
}

void edgeRequestGen(hls::stream<bitmap_block16_interval>         &input,
		hls::stream<edge_request>         &output
		){
	//int total=0;
	while(true){
		bitmap_block16_interval in;
		read_from_stream(input, in);
		if(in.flag==1){
			break;
		}
		for(int j=in.csr_begin;j<=in.csr_end;j++){
#pragma HLS PIPELINE II=1 rewind
			edge_request out;
			out.offset=in.offset;
			out.data=in.data;
			out.edgeAddr=j;
			out.flag=0;
			write_to_stream(output, out);
		}
		//total+=in.csr_end-in.csr_begin+1;
		//printf("offset %d:from %d to %d---size=%d total=%d\n",
				//in.offset,in.csr_begin,in.csr_end,in.csr_end-in.csr_begin+1,total);
	}
	{
		edge_request out;
		out.data=0;
		out.offset=0;
		out.edgeAddr=0;
		out.flag=1;
		write_to_stream(output, out);
	}
}

typedef struct{
	uint edgeAddr;
	ap_uint<1>                    flag;
}mem_request;
void memRequestGen(hls::stream<edge_request>         &input,
		hls::stream<edge_request>         &output,
		hls::stream<mem_request>         &output1,
		hls::stream<mem_request>         &output2,
		hls::stream<mem_request>         &output3,
		hls::stream<mem_request>         &output4){
	while(true){
		#pragma HLS PIPELINE II=1
		edge_request in;
		read_from_stream(input, in);
		edge_request out=in;
		mem_request out1,out2,out3,out4;
		out1.edgeAddr=(in.edgeAddr/32*8+(in.edgeAddr%8))*4;
		out2.edgeAddr=(in.edgeAddr/32*8+(in.edgeAddr%8))*4;
		out3.edgeAddr=(in.edgeAddr/32*8+(in.edgeAddr%8))*4;
		out4.edgeAddr=(in.edgeAddr/32*8+(in.edgeAddr%8))*4;
		out1.flag=in.flag;
		out2.flag=in.flag;
		out3.flag=in.flag;
		out4.flag=in.flag;
		write_to_stream(output, out);
		ap_uint<2> select=((ap_uint<32>)in.edgeAddr).range(4,3);
		if(/*in.edgeAddr%32>=0*8&&in.edgeAddr%32<1*8*/select==0||in.flag) write_to_stream(output1, out1);
		if(/*in.edgeAddr%32>=1*8&&in.edgeAddr%32<2*8*/select==1||in.flag) write_to_stream(output2, out2);
		if(/*in.edgeAddr%32>=2*8&&in.edgeAddr%32<3*8*/select==2||in.flag) write_to_stream(output3, out3);
		if(/*in.edgeAddr%32>=3*8&&in.edgeAddr%32<4*8*/select==3||in.flag) write_to_stream(output4, out4);

		if(in.flag==1){
			break;
		}
	}
}

/*void edgeRead(uint16	*edge,
		hls::stream<mem_request>         &input,
		hls::stream< ap_uint<X_WIDTH*16> >         &output){
#pragma HLS function_instantiate variable=input

	while(true){
		#pragma HLS PIPELINE II=1
		mem_request in;
		read_from_stream(input, in);
		if(in.flag==1){
			break;
		}
		uint16 dram_read;
		{
			//#pragma HLS latency min=16 max=16
			dram_read=edge[in.edgeAddr];
		}
		ap_uint<X_WIDTH*16> out;
		for(int k = 0; k < 16; k ++) {
			#pragma HLS UNROLL
			out.range(k*X_WIDTH+X_WIDTH-1,k*X_WIDTH)=dram_read.range(k*32+X_WIDTH-1,k*32);
		}
		write_to_stream(output, out);
	}
}*/

/*#define EDGE_MEM_READ_BURST_SIZE 32
void edgeRead(uint16	*edge,
		hls::stream<mem_request>         &input,
		hls::stream< ap_uint<X_WIDTH*16> >         &output){
#pragma HLS function_instantiate variable=input

	ap_uint<X_WIDTH*16> buffer[512];

	uint last_edgeAddr=0xffffffff;
	mem_request in;
	while(true){
		while(true){
			#pragma HLS PIPELINE II=1
			read_from_stream(input, in);
			if(in.flag||in.edgeAddr/EDGE_MEM_READ_BURST_SIZE!=last_edgeAddr/EDGE_MEM_READ_BURST_SIZE){
				break;
			}
			write_to_stream(output, buffer[in.edgeAddr%EDGE_MEM_READ_BURST_SIZE]);
		}

		for(int k=0;k<EDGE_MEM_READ_BURST_SIZE;k++){
			#pragma HLS PIPELINE II=1
			uint16 dram_read=edge[in.edgeAddr/EDGE_MEM_READ_BURST_SIZE*EDGE_MEM_READ_BURST_SIZE+k];
			ap_uint<X_WIDTH*16> out;
			for(int k = 0; k < 16; k ++) {
				#pragma HLS UNROLL
				out.range(k*X_WIDTH+X_WIDTH-1,k*X_WIDTH)=dram_read.range(k*32+X_WIDTH-1,k*32);
			}
			buffer[k]=out;
		}
		if(in.flag==1){
			break;
		}
		write_to_stream(output, buffer[in.edgeAddr%EDGE_MEM_READ_BURST_SIZE]);
		last_edgeAddr=in.edgeAddr;
	}
}
*/
typedef struct{
	uint edgeAddr;
	ap_uint<EDGE_MEM_READ_BURST_SIZE/4> active_flag;
	ap_uint<1>                    flag;
}mem_burst;

void edgeReadPrepare(hls::stream<mem_request>         &memrequestStream,
		hls::stream< mem_burst >         &memburstStream){
#pragma HLS function_instantiate variable=memrequestStream

	uint last_edgeAddr=0xffffffff;
	ap_uint<EDGE_MEM_READ_BURST_SIZE/4> active_flag=0;
	mem_request in;
	while(true){
		while(true){
			#pragma HLS PIPELINE II=1
			read_from_stream(memrequestStream, in);
			if(in.flag||in.edgeAddr/(EDGE_MEM_READ_BURST_SIZE)!=last_edgeAddr/(EDGE_MEM_READ_BURST_SIZE)){
				break;
			}
			uint loc=(in.edgeAddr%(EDGE_MEM_READ_BURST_SIZE))/4;
			active_flag.range(loc,loc)=1;
		}
		if(active_flag){
			mem_burst out;
			out.edgeAddr=last_edgeAddr;
			out.active_flag=active_flag;
			out.flag=0;
			write_to_stream(memburstStream, out);

			//printf("burst_read:addr:%d num:%d flag:%d\n",out.edgeAddr,out.num,(int)in.flag);
		}
		if(in.flag==1){
			break;
		}
		{
			active_flag=0;
			last_edgeAddr=in.edgeAddr;
			uint loc=(in.edgeAddr%(EDGE_MEM_READ_BURST_SIZE))/4;
			active_flag.range(loc,loc)=1;
		}

	}
	{
		mem_burst out;
		out.edgeAddr=0;
		out.active_flag=0;
		out.flag=1;
		write_to_stream(memburstStream, out);
	}
}

typedef struct {
	ap_uint<X_WIDTH*16>   			      data;
    ap_uint<1>                    flag;
} edge_block16;
void edgeRead(uint16	*edge,
		hls::stream<mem_burst>         &memburstStream,
		hls::stream< edge_block16 >         &output){
#pragma HLS function_instantiate variable=memburstStream

	//int edge_ideal_count=0;
	//int edge_total_count=0;

	while(true){
		mem_burst in;
		read_from_stream(memburstStream, in);
		if(in.flag==1){
			break;
		}

		//edge_ideal_count+=cur.num*16;
		//edge_total_count+=EDGE_MEM_READ_BURST_SIZE*16;

		for(int k=0;k<EDGE_MEM_READ_BURST_SIZE;k++){
			#pragma HLS PIPELINE II=1

			uint16 dram_read=edge[in.edgeAddr/EDGE_MEM_READ_BURST_SIZE*EDGE_MEM_READ_BURST_SIZE+k];
			edge_block16 out;
			for(int kk = 0; kk < 16; kk ++) {
				#pragma HLS UNROLL
				out.data.range(kk*X_WIDTH+X_WIDTH-1,kk*X_WIDTH)=dram_read.range(kk*32+X_WIDTH-1,kk*32);
			}
			out.flag=0;
			if(in.active_flag.range(k/4,k/4)){
				write_to_stream(output, out);
			}
		}
	}

	{
		edge_block16 out;
		out.data=0;
		out.flag=1;
		write_to_stream(output, out);
		write_to_stream(output, out);
		write_to_stream(output, out);
		write_to_stream(output, out);
	}

	//printf("edge_ideal_count:%d edge_total_count:%d\n",edge_ideal_count,edge_total_count);
}

void edgeReadMerge(hls::stream< edge_block16 >         &input,
		hls::stream< ap_uint<X_WIDTH*EDGE_NUM> >         &output){
#pragma HLS function_instantiate variable=input
	while(true){
#pragma HLS PIPELINE II=4
		edge_block16 in1,in2,in3,in4;
		read_from_stream(input, in1);
		read_from_stream(input, in2);
		read_from_stream(input, in3);
		read_from_stream(input, in4);
		if(in1.flag==1&&in2.flag==1&&in3.flag==1&&in4.flag==1){
			break;
		}
		ap_uint<X_WIDTH*EDGE_NUM> out;
		out.range(1*16*X_WIDTH-1,0*16*X_WIDTH)=in1.data;
		out.range(2*16*X_WIDTH-1,1*16*X_WIDTH)=in2.data;
		out.range(3*16*X_WIDTH-1,2*16*X_WIDTH)=in3.data;
		out.range(4*16*X_WIDTH-1,3*16*X_WIDTH)=in4.data;
		write_to_stream(output, out);
	}
}

typedef struct {
	uint edgeAddr;
	ap_uint<EDGE_NUM>    			      data;
    ap_uint<1>                    flag;
} edge_requestWithIndex;
void csrIndexPrepare(hls::stream<edge_request>         &requestStream,
		hls::stream<uint16>			&indexStream,
		hls::stream<edge_requestWithIndex>         &requestWithIndexStream){
	uint last_offset=0xffffffff;
	uint16 last_csrIndex=0;
	while(true){
		#pragma HLS PIPELINE II=1
		edge_request in;
		read_from_stream(requestStream, in);
		if(in.flag==1){
			break;
		}
		if(in.offset!=last_offset){
			read_from_stream(indexStream, last_csrIndex);
			last_offset=in.offset;
		}
		uint16 csrIndex=last_csrIndex;

		ap_uint<17*7> csrIndex2;
		for(int k = 0; k < 17; k ++) {
			#pragma HLS UNROLL
			if(csrIndex.range(k*30+29, k*30)<in.edgeAddr*EDGE_NUM){
				csrIndex2.range(7*k+6,7*k)=0;
			}
			else if(csrIndex.range(k*30+29, k*30)>in.edgeAddr*EDGE_NUM+EDGE_NUM){
				csrIndex2.range(7*k+6,7*k)=EDGE_NUM;
			}
			else{
				csrIndex2.range(7*k+6,7*k)=csrIndex.range(k*30+29, k*30)-in.edgeAddr*EDGE_NUM;
			}
		}

		ap_uint<EDGE_NUM> src_value;
		for(int k = 0; k < EDGE_NUM; k ++) {
			#pragma HLS UNROLL
			ap_uint<16> set_flag;
			for(int kk=0;kk<16;kk++){
				#pragma HLS UNROLL
				if(k>=csrIndex2.range((kk)*7+6, (kk)*7)
						&&k<csrIndex2.range((kk+1)*7+6, (kk+1)*7)){
					set_flag.range(kk,kk)=1;
				}
				else{
					set_flag.range(kk,kk)=0;
				}
			}
			if((set_flag&in.data)){
				src_value.range(k,k)=1;
			}
			else{
				src_value.range(k,k)=0;
			}
		}

		edge_requestWithIndex out;
		out.edgeAddr=in.edgeAddr;
		out.data=src_value;
		out.flag=0;
		write_to_stream(requestWithIndexStream, out);
	}
	{
		edge_requestWithIndex out;
		out.edgeAddr=0;
		out.data=0;
		out.flag=1;
		write_to_stream(requestWithIndexStream, out);
	}
}

void edgeMerge(hls::stream< ap_uint<X_WIDTH*EDGE_NUM> >	(&edgesMergeStream)[4],
		hls::stream<edge_requestWithIndex>         &requestWithIndexStream,
		hls::stream<edge_tuples_t>         &edgeTuplesBuffer
		){

	uint last_edgeAddr=0xffffffff;
	ap_uint<X_WIDTH*EDGE_NUM> edge=0;
	while(true){
#pragma HLS PIPELINE II=1
		edge_requestWithIndex in;
		read_from_stream(requestWithIndexStream, in);
		if(in.flag==1){
			break;
		}
		ap_uint<2> select=((ap_uint<32>)in.edgeAddr).range(4,3);
		//if(in.edgeAddr%32>=0*8&&in.edgeAddr%32<1*8) select=0;
		//else if(in.edgeAddr%32>=1*8&&in.edgeAddr%32<2*8) select=1;
		//else if(in.edgeAddr%32>=2*8&&in.edgeAddr%32<3*8) select=2;
		//else if(in.edgeAddr%32>=3*8&&in.edgeAddr%32<4*8) select=3;

		if(in.edgeAddr!=last_edgeAddr){
			read_from_stream(edgesMergeStream[select], edge);
			last_edgeAddr=in.edgeAddr;
		}

		edge_tuples_t tuples;
		tuples.datax=edge;
		tuples.datay=in.data;
		tuples.flag = 0;
		write_to_stream(edgeTuplesBuffer, tuples);
	}
	{
		edge_tuples_t tuples;
		tuples.datax=0;
		tuples.datay=0;
		tuples.flag=1;
		write_to_stream(edgeTuplesBuffer, tuples);
	}
}

void srcPropertyProcess(
						 uint16                             *bitMap,
						 uint16                             *csrIndex1,
						 uint16                             *csrIndex2,
						 //uint16                             *csrIndex3,
						 //uint16                             *csrIndex4,
						 uint16                       		*edges1,
						 uint16                             *edges2,
						 uint16                             *edges3,
						 uint16                             *edges4,
						 int                			srcStart,
						 int                			srcEnd,
                         hls::stream<edge_tuples_t>         &edgeTuplesBuffer
                       )
{
#pragma HLS DATAFLOW
	hls::stream<csrIndex_mem_request>         csrIndexMemRequestStream[2];
    #pragma HLS stream variable=csrIndexMemRequestStream depth=16
    #pragma HLS RESOURCE variable=csrIndexMemRequestStream core=FIFO_SRL

	hls::stream<csrIndex_select>         csrIndexSelectStream;
	#pragma HLS stream variable=csrIndexSelectStream depth=16
    #pragma HLS RESOURCE variable=csrIndexSelectStream core=FIFO_SRL

    hls::stream<uint16>	csrIndexReadStream[2];
    #pragma HLS stream variable=csrIndexReadStream depth=16
    #pragma HLS RESOURCE variable=csrIndexReadStream core=FIFO_SRL

    hls::stream<bitmap_block16_withIndex>	bitMapWithIndexStream;
    #pragma HLS stream variable=bitMapWithIndexStream depth=16
    #pragma HLS RESOURCE variable=bitMapWithIndexStream core=FIFO_SRL

    hls::stream<intervalBlock4>    intervalBlock4Stream;
    #pragma HLS stream variable=intervalBlock4Stream depth=16
    #pragma HLS RESOURCE variable=intervalBlock4Stream core=FIFO_SRL

    hls::stream<intervalBlock2>    intervalBlock2Stream;
    #pragma HLS stream variable=intervalBlock2Stream depth=16
    #pragma HLS RESOURCE variable=intervalBlock2Stream core=FIFO_SRL

    hls::stream<bitmap_block16_interval>         intervalStream;
    #pragma HLS stream variable=intervalStream depth=16
    #pragma HLS RESOURCE variable=intervalStream core=FIFO_SRL

    hls::stream< edge_request >         requestStream;
    #pragma HLS stream variable=requestStream depth=16
    #pragma HLS RESOURCE variable=requestStream core=FIFO_SRL

    hls::stream< edge_request >         dumprequestStream;
    #pragma HLS stream variable=dumprequestStream depth=512
    #pragma HLS RESOURCE variable=dumprequestStream core=FIFO_BRAM

    hls::stream<mem_request>         memrequestStream[4];
    #pragma HLS stream variable=memrequestStream depth=512
    #pragma HLS RESOURCE variable=memrequestStream core=FIFO_BRAM

    hls::stream<mem_burst>         memburstStream[4];
	#pragma HLS stream variable=memburstStream depth=16
    #pragma HLS RESOURCE variable=memburstStream core=FIFO_SRL

    hls::stream< edge_block16 >			edgesStream[4];
    #pragma HLS stream variable=edgesStream depth=512
    #pragma HLS RESOURCE variable=edgesStream core=FIFO_BRAM

    hls::stream< ap_uint<X_WIDTH*EDGE_NUM> >			edgesMergeStream[4];
    #pragma HLS stream variable=edgesMergeStream depth=8
    #pragma HLS RESOURCE variable=edgesMergeStream core=FIFO_SRL

    hls::stream<uint16>			indexStream;
    #pragma HLS stream variable=indexStream depth=512
    #pragma HLS RESOURCE variable=indexStream core=FIFO_BRAM

    hls::stream< edge_requestWithIndex >         requestWithIndexStream;
    #pragma HLS stream variable=requestWithIndexStream depth=16
    #pragma HLS RESOURCE variable=requestWithIndexStream core=FIFO_SRL

    bitMapRead(bitMap,csrIndexMemRequestStream,csrIndexSelectStream,srcStart,srcEnd);
    csrIndexRead(csrIndex1,csrIndexMemRequestStream[0],csrIndexReadStream[0]);
    csrIndexRead(csrIndex2,csrIndexMemRequestStream[1],csrIndexReadStream[1]);
    //csrIndexRead(csrIndex3,csrIndexMemRequestStream[2],csrIndexReadStream[2]);
    //csrIndexRead(csrIndex4,csrIndexMemRequestStream[3],csrIndexReadStream[3]);
    csrIndexReadMerge(csrIndexReadStream,csrIndexSelectStream,bitMapWithIndexStream);

    edgeRequestMerge(bitMapWithIndexStream,intervalBlock4Stream,indexStream);
    intervalFilter4to2(intervalBlock4Stream,intervalBlock2Stream);
    intervalFilter2to1(intervalBlock2Stream,intervalStream);
    edgeRequestGen(intervalStream,requestStream);
    memRequestGen(requestStream,dumprequestStream,
    	   memrequestStream[0],memrequestStream[1],memrequestStream[2],memrequestStream[3]);

    edgeReadPrepare(memrequestStream[0],memburstStream[0]);
    edgeReadPrepare(memrequestStream[1],memburstStream[1]);
    edgeReadPrepare(memrequestStream[2],memburstStream[2]);
    edgeReadPrepare(memrequestStream[3],memburstStream[3]);

    edgeRead(edges1,memburstStream[0],edgesStream[0]);
    edgeRead(edges2,memburstStream[1],edgesStream[1]);
    edgeRead(edges3,memburstStream[2],edgesStream[2]);
    edgeRead(edges4,memburstStream[3],edgesStream[3]);

    edgeReadMerge(edgesStream[0],edgesMergeStream[0]);
    edgeReadMerge(edgesStream[1],edgesMergeStream[1]);
    edgeReadMerge(edgesStream[2],edgesMergeStream[2]);
    edgeReadMerge(edgesStream[3],edgesMergeStream[3]);

    csrIndexPrepare(dumprequestStream,indexStream,requestWithIndexStream);
    edgeMerge(edgesMergeStream,requestWithIndexStream,edgeTuplesBuffer);
}


/*void srcPropertyProcess( uint16                             *vertexScore,
                         hls::stream<burst_token>           &edgeBurstStream,
                         hls::stream<burst_token>           &mapStream,
                         hls::stream<edge_tuples_t>         &edgeTuplesBuffer
                       )
{

	while(true){
		burst_token map;
		burst_token edge;
		edge_tuples_t tuples;
		read_from_stream(mapStream, map);
		read_from_stream(edgeBurstStream, edge);

		readCacheInner: for (int k = 0; k < EDGE_NUM; k ++) {
		#pragma HLS UNROLL

			tuples.datax.range(k*X_WIDTH+X_WIDTH-1,k*X_WIDTH)=edge.data.range(32*k+X_WIDTH-1,32*k);
			tuples.datay.range(k*Y_WIDTH+Y_WIDTH-1,k*Y_WIDTH)=map.data.range(32*k+NODE_WIDTH-1,32*k+NODE_WIDTH-1);

			//if(tuples.datax.range(k*X_WIDTH+X_WIDTH-1,k*X_WIDTH)<16)
				//printf("dst=%x,src vertex=%x\n",(int)tuples.datax.range(k*X_WIDTH+X_WIDTH-1,k*X_WIDTH)
						//,(int)tuples.datay.range(k*Y_WIDTH+Y_WIDTH-1,k*Y_WIDTH));

		}
		tuples.flag = (edge.flag);
		write_to_stream(edgeTuplesBuffer, tuples);
		if(tuples.flag==1){
			break;
		}
	}
}*/

#endif /* __FPGA_CACHE_H__ */

